/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.qudexapps.xmllib.services;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.igfay.jfig.JFig;
import org.igfay.jfig.JFigException;

import fedora.fedoraSystemDef.foxml.DatastreamType;
import fedora.fedoraSystemDef.foxml.StateType;
import fedora.fedoraSystemDef.foxml.DigitalObjectDocument.DigitalObject;
import uk.da.dext.CategoryCollectionType;
import uk.da.dext.CategoryType;
import uk.da.dext.QudexDocument;
import uk.da.dext.datastructs.CategoryStruct;
import uk.da.dext.datastructs.CodeStruct;
import uk.da.dext.datastructs.DocumentStruct;
import uk.da.dext.datastructs.MemoStruct;
import uk.da.dext.exceptions.QudexException;
import uk.da.dext.genxml.QudexManagement;
import uk.ljmu.fedoraifz.restrequests.FedoraOperationsAPIM;
import uk.ljmu.metadata.schemas.SchemaElem;
import uk.ljmu.metadata.schemas.SchemaInstance;
import uk.ljmu.metadata.schemasutilities.DcUtilities;
import uk.ljmu.metadata.schemasutilities.SchemasProcessing;
import uk.ljmu.metadata.utilities.MetadataUtilities;
import uk.ljmu.metadata.utilities.MetadataUtilities.ValueType;
import uk.ljmu.qudexapps.xmllib.exceptions.FoxmlException;
import uk.ljmu.qudexapps.xmllib.model.CollectionElement;
import uk.ljmu.qudexapps.xmllib.model.ColumnMap;
import uk.ljmu.qudexapps.xmllib.model.FoxmlBeans;
import uk.ljmu.qudexapps.xmllib.utilities.Constants;
import uk.ljmu.qudexapps.xmllib.utilities.IngestionResults;
import uk.ljmu.qudexapps.xmllib.utilities.SpreadsheetUtilities;
import uk.ljmu.qudexapps.xmllib.utilities.SpreadsheetUtilities.Column;
import uk.ljmu.qudexapps.xmllib.validators.QudexFoxmlValidatorImpl;
import uk.ljmu.qudexapps.xmllib.validators.QudexFoxmlValidator;

/**
 * Implements the interface for the management of QuDeX generation from Fedora
 * Objects
 * 
 * @author agustinamartinez
 * 
 */
public class QudexFoxmlServiceImpl implements QudexFoxmlService {
    /**
     * ROOT LOGGER
     */
    private static Logger log = Logger.getLogger(QudexFoxmlServiceImpl.class);

    /**
     * String representing the type of the working spreadsheet (excel or CSV)
     */
    private String fileType;
    /**
     * Spreadsheet Management instance
     */
    private SpreadsheetManagement spService;
    /**
     * Foxml beans instance to hold the FOXML file in memory
     */
    private FoxmlBeans foxmlBeans;
   
    /**
     * Variables used to track execution times for the methods, used when
     * creating UI progress bars
     */
    /**
     * Variables used to give to the UI information about current execution
     * progress in percentages
     */
    private String progressStatus = "0%";
    /**
     * Manage optionality of codes, categories and memos
     */
    boolean isCategoriesPresent;
    boolean isCodesPresent;
    boolean isMemosPresent;
    /**
     * Boolean to assign the appropriate validator: qudexCollections or
     * qudexFiles
     */
    private boolean isQudexCollections = false;
   
    /**
     * Manager for the Qudex XML instance generation
     */
    private QudexManagement qudexManager;
    
    /**
     * Object that manages Fedora related operations, e.g. collection creation, add datastreams
     */
    private QudexFedoraObjects quObject;
    
    /**
     * Management of collection, sub-collections and document relationships
     * isPartOf mainly between the different high-level elements. Does not apply
     * to codes, memos and categories.
     */
    private RelationshipsManagement relsManagement;

    private String originalSpreadsheetName;

    /**
     * @param isQudexCollections
     *            the isQudexCollections to set
     */
    public void setQudexCollections(boolean isQudexCollections) {
	this.isQudexCollections = isQudexCollections;
    }

    /**
     * @return the isQudexCollections
     */
    public boolean isQudexCollections() {
	return isQudexCollections;
    }

    /**
     * @param progressStatus
     *            the progressStatus to set
     */
    public void setProgressStatus(String progressStatus) {
	this.progressStatus = progressStatus;
    }

    /**
     * Returns the actual execution percentage
     * 
     * @return the progressStatus
     */
    public String getProgressStatus() {
	return progressStatus;
    }

    /**
     * @return the foxmlBeans
     */
    public FoxmlBeans getFoxmlBeans() {
	return foxmlBeans;
    }

    /**
     * @param foxmlBeans
     *            the foxmlBeans to set
     */
    public void setFoxmlBeans(FoxmlBeans foxmlBeans) {
	this.foxmlBeans = foxmlBeans;
    }

    /**
     * @param spService
     *            the spService to set
     */
    public void setSpService(SpreadsheetManagement spService) {
	this.spService = spService;
    }

    /**
     * @return the spService
     */
    public SpreadsheetManagement getSpService() {
	return spService;
    }

    /**
     * @param objResults
     *            the objResults to set. Handles String results
     */
    public void setObjResults(List<IngestionResults> objResults) {
	this.quObject.setObjResults(objResults);
    }

    /**
     * Gets the type of file we are dealing in the request: csv or excel
     * 
     * @return the _typeFile
     */
    public String getFileType() {
	return fileType;
    }

    /**
     * @param file
     *            the _typeFile to set
     */
    public void setFileType(String file) {
	fileType = file;
    }

    /**
     * @return the objResults
     */
    public List<IngestionResults> getObjResults() {
	return this.quObject.getObjResults();
    }

    /**
     * @param qudexManager
     *            the qudexManager to set
     */
    public void setQudexManager(QudexManagement qudexManager) {
	this.qudexManager = qudexManager;
    }

    /**
     * @return the qudexManager
     */
    public QudexManagement getQudexManager() {
	return qudexManager;
    }

    /**
     * @param quObject the quObject to set
     */
    public void setQuObject(QudexFedoraObjects quObject) {
	this.quObject = quObject;
    }

    /**
     * @return the quObject
     */
    public QudexFedoraObjects getQuObject() {
	return quObject;
    }

    /**
     * @param relsManagement the relsManagement to set
     */
    public void setRelsManagement(RelationshipsManagement relsManagement) {
	this.relsManagement = relsManagement;
    }

    /**
     * @return the relsManagement
     */
    public RelationshipsManagement getRelsManagement() {
	return relsManagement;
    }

    // CONSTRUCTORS
    /**
     * Constructor
     * 
     * @param typeSpreadsheet EXCEL file or CSV file mimetype
     * @param isCollections boolean that specifies whether the current managed spreadsheet
     * 		is a collections/sub-collections spreadsheet
     * @throws FoxmlException
     * @throws JFigException
     * 
     */
    public QudexFoxmlServiceImpl(String typeSpreadsheet, boolean isCollections)
	    throws FoxmlException, JFigException {

	this.fileType = typeSpreadsheet;
	this.quObject = new QudexFedoraObjects();
	this.quObject.setFedoraUser(JFig.getInstance().getValue("fedoraServer", "user"));
	this.foxmlBeans = new FoxmlBeans(this.quObject.getFedoraObject());
	this.progressStatus = "0%";
	this.isQudexCollections = isCollections;
	this.qudexManager = new QudexManagement();
	this.relsManagement = new RelationshipsManagement();
    }

    /**
     * Custom constructor
     * 
     * @param typeSpreadsheet
     *            EXCEL file or CSV file mimetype
     * @param fedObj
     *            instance of the Fedora communication Object
     * @param qudexCols
     *            boolean that specifies whether the current managed spreadsheet is a collections/sub-collections spreadsheet
     * @throws FoxmlException
     * @throws JFigException
     */
    public QudexFoxmlServiceImpl(String typeSpreadsheet,
	    FedoraOperationsAPIM fedObj, boolean qudexCols)
	    throws FoxmlException, JFigException {

	this.fileType = typeSpreadsheet;
	this.quObject = new QudexFedoraObjects(fedObj);
	this.quObject.setFedoraUser(JFig.getInstance().getValue("fedoraServer", "user"));
	this.foxmlBeans = new FoxmlBeans(this.quObject.getFedoraObject());
	this.progressStatus = "0%";
	this.qudexManager = new QudexManagement();
	this.isQudexCollections = qudexCols;
	this.relsManagement = new RelationshipsManagement();
    }

    /**
     * Custom constructor
     * 
     * @param typeSpreadsheet
     *            EXCEL file or CSV file mimetype
     * @param user
     *            the user to connect to fedora
     * @param password
     *            the password for the fedora user
     * @param qudeCols
     *            boolean expressing whether documents spreadsheet or
     *            collections spreadsheet
     * @throws FoxmlException
     * @throws JFigException
     */
    public QudexFoxmlServiceImpl(String typeSpreadsheet, String user,
	    String password, boolean qudexCols) throws FoxmlException,
	    JFigException {

	this.fileType = typeSpreadsheet;
	this.quObject = new QudexFedoraObjects(user, password);
	this.quObject.setFedoraUser(user);
	this.foxmlBeans = new FoxmlBeans(this.quObject.getFedoraObject());
	this.progressStatus = "0%";
	this.qudexManager = new QudexManagement();
	this.isQudexCollections = qudexCols;
	this.relsManagement = new RelationshipsManagement();
    }

    /**
     * Create an empty FOXML XML document with auto-generated PID
     * 
     * @param namespace
     *            [optional] namespace used to generate the object PID
     * @param xmlFormat
     *            true if we want the results of the request in XML format
     * 
     * @return FoxmlBeans empty object with a new PID assigned
     */
    private void createFoxmlFile(String namespace, String xmlFormat)
	    throws FoxmlException {
	String pid = "";
	String pidListXML = "";
	DigitalObject digObj;

	// AMG- Obtain current Foxml Digital Object instance
	digObj = (this.foxmlBeans.getFoxmlDoc().getDigitalObject() != null) ? this.foxmlBeans
		.getFoxmlDoc().getDigitalObject()
		: this.foxmlBeans.getFoxmlDoc().addNewDigitalObject();
	// Add OWNER
	this.foxmlBeans.addOwnerIDProperty(this.quObject.getFedoraUser());

	log.info("Creating new PID for the Object...");

	// AMg- Parameters: number of PIDs, optional namespace associate to the
	// PID, XML output format
	pidListXML = this.quObject.getFedoraObject().getNextPID(1, namespace, xmlFormat);

	if (pidListXML == null || pidListXML == "") {
	    // NO PID GENERATED
	    // ERROR Exit
	    log.fatal("NO NEW PID generated, in method _CreateFoxmlFile");
	    throw new FoxmlException(
		    "NO NEW PID generated, in method createFoxmlFile");
	}

	// AMG- Parse XML response to obtain the generated PID
	try {
	    Document doc = DocumentHelper.parseText(pidListXML);
	    Element pidElem = doc.getRootElement().element("pid");
	    pid = pidElem.getText();
	    digObj.setPID(pid.trim());
	    log.info("New PID generated: " + pid.trim());
	} catch (DocumentException ex) {
	    log.fatal("In createFOXML: " + ex.getMessage());
	    throw new FoxmlException(
		    "PID not assigned, in method createFoxmlFile");
	}
    }

    /**
     * Create an empty FOXML XML document with auto-generated PID
     * 
     * @param pid
     *            the Fedora object PID
     * 
     * @return FoxmlBeans empty object with a new PID assigned
     */
    private void createFoxmlFile(String pid) throws FoxmlException {
	DigitalObject digObj;

	// Check if Object Exist in the repository
	String foxml = this.quObject.getFedoraObject().getObjectXML(pid);
	if (foxml.contains(Constants.XML_UTF_VERSION_HEADER)) {
	    // AMG- Obtain current Foxml Digital Object instance
	    digObj = (this.foxmlBeans.getFoxmlDoc().getDigitalObject() != null) ? this.foxmlBeans
		    .getFoxmlDoc().getDigitalObject()
		    : this.foxmlBeans.getFoxmlDoc().addNewDigitalObject();
	    // Add OWNER
	    this.foxmlBeans.addOwnerIDProperty(this.quObject.getFedoraUser());
	    digObj.setPID(pid.trim());
	} else {
	    throw new FoxmlException(
		    "Object not present in the repository. PID: " + pid);
	}
    }

    /**
     * Ingest in the repository Qudex Collection Objects (at least one)
     * @param collectionsIds list of String containing the PID of the parent collection (only one collection)
     * @param collectionSs
     *            inputstream instance with the collection level spreadsheet
     * 
     * @throws FoxmlException
     *             if any errors while processing the spreadsheet or ingesting
     *             objects into the repository
     */
    public void ingestQudexCollectionsSpreadsheet(List<String> collectionsIds, InputStream collectionSs)
	    throws FoxmlException {
	StringBuilder sb = new StringBuilder();
	int leftNumberPasses = 0, counter = 1;
	float increment = 0;
	boolean resultsOK = true, deletePid, updatePid, collFromSpreadsheet = false, collectionFromApp = false;
	@SuppressWarnings("unused")
	String existingObjectPid = "", rootCollection="";

	// Clean array of results from previous iterations
	this.quObject.getObjResults().clear();
	
	this.progressStatus = "0%";
	// Init FedoraObject in FoxmlBeans
	this.foxmlBeans.initFedoraObject(this.quObject.getFedoraObject());
	resultsOK = getSpreadsheetManager();
	if (!resultsOK)
	    throw new FoxmlException("File is neither CSV or EXCEL...");
	try {
	    // AMG- (1) CREATE MAP STRUCTURE FROM XLS OR CSV FILE
	    try {
		resultsOK = spService.readSpreadsheet(collectionSs);
		//Estimated time for reading spreadsheet
		this.progressStatus = "20%";
		if (!resultsOK) {
		    this.quObject.addFedoraResultsMessage("spreadsheetManager",
			    "Error in ingestQudexCollectionsSpreadsheet");
		    log.fatal("Errors while parsing spreadsheet...");
		    return;
		}
	    } catch (Exception ex) {
		sb
			.append("In readSpreadsheet: Map structure from spreadsheet not created ");
		sb.append(ex.getMessage());
		log.fatal(sb.toString());
		// AMG- ADD RESULTS ERROR OBJECT
		this.quObject.addFedoraResultsMessage(
			"manager", "Errors while parsing spreadsheet. Revise spreadsheet format.");
		return;
	    }
	    // AMG- Progress Bar
	    leftNumberPasses = this.spService.getSpreadsheetMap()
		    .getGlobalMap().size();
	    increment = (80 / (float) leftNumberPasses);
	    //Manage sub-collections parents
	    if (collectionsIds.size() >= 1 && !collectionsIds.get(0).equals("")) {
		collectionFromApp = true;
		// Check collection validity, the sub-collection can't be attached to a collection which already presents
		//a Qudex Instance Datastream
		resultsOK = this.quObject.isCollectionWithQudexInstance(collectionsIds.get(0));
		if (resultsOK) {
		    this.quObject.addFedoraResultsMessage(
				"manager",
				"The selected Qudex Collection already presents Qudex Documents. Specify a different Collection PID");
		    return; 
		}
		resultsOK = this.relsManagement.setDocumentsParentCollection(collectionsIds.get(0),
			uk.da.dext.utilities.Constants.FEDORA_IS_PART, this.getFedoraObject());
		if (!resultsOK) {
		    // Collection is not a valid collection, then abort
		    throw new FoxmlException("Container collection with PID: "
			    + collectionsIds.get(0)
			    + " is not a valid collection.");
		} else collFromSpreadsheet = true;                                                                                                       
		//Find whether is a sub-collection or not
		@SuppressWarnings("unused")
		String auxCollection = this.relsManagement.getRootCollection(collectionsIds.get(0));
		/*AMG COMMENTED not including now root collection relationship*/
		//if (auxCollection == null) {
		    //Errors finding root collection
		    //this.quObject.addFedoraResultsMessage(
			//"manager",
			//"Errors while retrieving collection relationship. Check the specified parent collection.");
		    //return;
		//} else if (!auxCollection.equals(collectionsIds.get(0))) {
		  //  resultsOK = this.quObject.isCollectionWithQudexInstance(auxCollection);
			//if (resultsOK) {
			  //  this.quObject.addFedoraResultsMessage(
				//	"manager",
					//"The selected Qudex Collection already presents Qudex Documents. Specify a different Collection PID");
			    //return; 
			//}
		 //   resultsOK = this.relsManagement.setDocumentsParentCollection(auxCollection,
			//	uk.da.dext.utilities.Constants.FEDORA_IS_PART, this.getFedoraObject());
		    //rootCollection = auxCollection;
		//}
	    }
	    
	    // AMG- EXTERNAL LOOP FOR EACH ROW IN THE MAP STRUCTURE
	    for (ColumnMap item : spService.getSpreadsheetMap().getGlobalMap()
		    .values()) {
		// AMG (16/11) Init FoxmlBeans Document
		this.foxmlBeans.initFoxmlDocumentObject();
		deletePid = false;
		updatePid = false;
		try {
		    // CHECK if update/delete operations in Object
		    deletePid = isDeleteOperation(item);
		    if (!deletePid) {
			updatePid = isUpdatePid(item);
			if (updatePid) {
			    // Find PID in spreadsheet, if not present ERROR
			    existingObjectPid = findCollectionPid(item.getIdentifier());
			    if (existingObjectPid == null) {
				log
				    .error("Errors when updating collection, Pid not present.");
				// AMG- Progress bar update
				//20% estimated for spreadsheet parsing initially
				 this.quObject.addFedoraResultsMessage("manager",
					    "Errors when updating Qudex Collection, Pid not present.");
				int auxInt = Math.round((float) increment*counter + 20);
				this.progressStatus = Long.toString(auxInt) + "%";
				counter++;
				continue;
			    }
			}
		    } else {
			// Delete Object and continue with next object
			// Find PID in spreadsheet, if not present ERROR
			existingObjectPid = findCollectionPid(item.getIdentifier());
			if (existingObjectPid == null) {
			    log
				    .error("Trying to delete a collection without PID.");
			    this.quObject.addFedoraResultsMessage("manager",
				    "Trying to delete a collection without specifying its PID.");
			} else {
			    resultsOK = this.quObject.deleteQudexCollectionRoot(existingObjectPid, true, this.relsManagement);
			    if (resultsOK) {
				this.quObject.addFedoraResultsMessage("manager", "Object with PID " + existingObjectPid + " deleted");
			    }
			}
			// AMG- Progress bar update
			//20% estimated for spreadsheet parsing initially
			int auxInt = Math.round((float) increment*counter + 20);
			this.progressStatus = Long.toString(auxInt) + "%";
			counter++;
			continue;
		    }
		    // AMG- (2) CREATE EMPTY FOXML OBJECT OR Existing OBJECT if
		    // update operation
		    if (!updatePid)
			this.createFoxmlFile(Constants.DEF_QUDEX_NS, "true");
		    else {
			// Update Operation on a existing object
			try {
			    this.createFoxmlFile(existingObjectPid);
			} catch (FoxmlException ex) {
			    this.quObject.addFedoraResultsMessage(
					    existingObjectPid,
					    "Error while updating object with PID: "
						    + existingObjectPid
						    + ". Object not present in the repository.");
			    // AMG- Progress bar update
			    //20% estimated for spreadsheet parsing initially
			    int auxInt = Math.round((float) increment*counter + 20);
			    this.progressStatus = Long.toString(auxInt) + "%";
			    counter++;
			    // Skip to next object
			    continue;
			}
		    }

		    String label = RelationshipsManagement.findLabel("title", item,
		    	    this.fileType);
		    sb.append(label != "" ? label : "QuDEx Collection Object");
		    this.foxmlBeans.addLabelProperty(sb.toString());
		    sb.delete(0, sb.toString().length());
		} catch (FoxmlException ex) {
		    sb.append("Error while creating Foxml instance: ");
		    sb.append(ex.getMessage());
		    sb.append("collection");
		    log.fatal(sb.toString());
		    this.quObject.addFedoraResultsMessage("manager",
				    "Errors while creating Foxml Document. Contact administrator.");
		    return;
		}
		/*
		 * AMG- THE STUDY FOXML OBJECT WILL CONTAIN A RELS-EXT AND A DC
		 * DATASTREAM BY DEFAULT NAMESPACES MANAGEMENT
		 */

		resultsOK = false;
		try {
		    resultsOK = addObjectMetadataElements(item, collectionFromApp);
		} catch (QudexException e) {
		    log.fatal(e.getMessage());
		}

		// AMG (16/11) FOXML NOT ADDED, skip to the next one
		if (!resultsOK) {
		    // AMG- LOG ERRORS
		    //sb.append("Document with PID: ");
		    //sb.append(this.foxmlBeans.getFoxmlDoc().getDigitalObject()
			//    .getPID());
		    //sb
			//    .append(" not created. Errors while adding metadata fields.");
		    //log.info(sb.toString());
		    //this.quObject.addFedoraResultsMessage("metadataError", sb
			//	    .toString());
		    //sb.delete(0, sb.toString().length());
		    continue;
		}
		/*
		 * AMG (4) If Content Models added, ADD here the code to create
		 * and manage RELS-EXT DS
		 */
		// AMG (5) Foxml Validation. Create an XmlOptions instance and
		// set the error listener.
		XmlOptions validateOptions = new XmlOptions();
		ArrayList<XmlError> errorList = new ArrayList<XmlError>();
		validateOptions.setErrorListener(errorList);
		resultsOK = this.foxmlBeans.getFoxmlDoc().validate(
			validateOptions);
		if (!resultsOK) {
		    for (int i = 0; i < errorList.size(); i++) {
			XmlError error = (XmlError) errorList.get(i);
			sb.append("RESULTS OF VALIDATION \nMessage: ");
			sb.append(error.getMessage());
			sb.append("\n");
			sb.append(error.getCursorLocation().xmlText());
			log.fatal(sb.toString());
			sb.delete(0, sb.toString().length());
		    }
		    log.fatal("Error while validating FOXML instance");
		    this.quObject.addFedoraResultsMessage("manager",
			    "Error while validating Document FOXML instance for object with pid " + 
			    this.foxmlBeans.getFoxmlDoc().getDigitalObject() + " title '" +
			    this.foxmlBeans.getFoxmlLabel() + "'.");
		    throw new FoxmlException(
			    "FOXML Document Schema validation failed!!!");
		}

		// AMG- (6) CALL TO INGEST METHOD
		sb.append(Constants.XML_VERSION_HEADER);
		sb.append(this.foxmlBeans.getFoxmlDoc().toString());
		String results = "";
		if (!updatePid) {
		    results = this.quObject.ingestObjectFedora("QuDEx Collection Object",
			    Constants.DEF_QUDEX_NS, sb
			    .toString(), this.foxmlBeans.getFoxmlDoc()
			    .getDigitalObject().getPID());
		    sb.delete(0, sb.toString().length());
		} else {
		    sb.delete(0, sb.toString().length());
		    results = this.quObject.updateObjectFedora(this.foxmlBeans);

		    if (results == null) {
			results = "Error updating object. Contact Administrator";
		    }
		}
		if (!results.startsWith(Constants.DEF_QUDEX_NS)) {
		    // RESULTS OBJECT CREATION AND PARSE OF INGESTION RESULTS
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
				    .getFoxmlDoc().getDigitalObject().getPID(),
				    "Errors while storing document in Fedora. Contact administrator.");
		} else {
		    if (updatePid) 
			this.quObject.addFedoraResultsMessage("manager", "Updated collection with Pid: " + results);
		    else {
			this.quObject.addFedoraResultsMessage("manager", "Created collection with Pid: " + results + 
				" and Title: " + this.foxmlBeans.getFoxmlLabel());
		    }
		}
		// AMG - Add relationships
		resultsOK = this.relsManagement.generateCollectionRelationships(this.foxmlBeans.getFoxmlDoc().getDigitalObject().getPID(), 
			this.getFedoraObject());
		if (!resultsOK) {
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
			    .getFoxmlDoc().getDigitalObject().getPID(),
			    "Errors while creating collection relationship information. Contact administrator.");
		}
		resultsOK = this.quObject.addSpreadsheetToObject("COLLECTION_SP", 
			this.foxmlBeans.getFoxmlDoc().getDigitalObject().getPID(), this.fileType);
		if (!resultsOK) {
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
			    .getFoxmlDoc().getDigitalObject().getPID(),
			    "Errors while adding Ingestion Spreadsheet to collection object.");
		}
		//IF coll coming from spreadsheet add it
		if (collFromSpreadsheet) {
		    resultsOK = this.relsManagement.setDocumentsParentCollection(collectionsIds.get(0),
			uk.da.dext.utilities.Constants.FEDORA_IS_PART, this.getFedoraObject());
		    //AMG COMMENTED ROOT COLLECTION REL NOW NOT INCLUDED
		    //if (!rootCollection.equals("")) {
			//resultsOK = this.relsManagement.setDocumentsParentCollection(rootCollection,
			//	uk.da.dext.utilities.Constants.FEDORA_IS_PART, this.getFedoraObject());
		    //}
		}
		// AMG- Progress bar update
		//20% estimated for spreadsheet parsing initially
		int auxInt = Math.round((float) increment*counter + 20);
		this.progressStatus = Long.toString(auxInt) + "%";
		counter++;
	    }
	    if (!this.progressStatus.equals("100%"))
		this.progressStatus = "100%";
	    // AMG- Give time to AJAX calls to update when using progress bars
	    // in the user interface
	    Thread.sleep(2000);

	} catch (InterruptedException e) {
	    log.fatal(e.getMessage());
	    this.quObject.addFedoraResultsMessage("manager", "Errors in application, contact administrator");
	} catch (JFigException e) {
	    log.fatal(e.getMessage());
	    this.quObject.addFedoraResultsMessage("manager", "Errors in application, contact administrator");
	} finally {
	    // Clean instances of maps
	    this.cleanInstance();
	}
    }

    /**
     * Ingest file level objects in the repository attached to a set of Fedora
     * collection objects (at least 1)
     * 
     * @param collectionIds
     *            array of fedora collection parents PIDs
     * @param fileLevelSs
     *            inputstream instance for the file level spreadsheet
     * 
     * @throws FoxmlException
     *             if any errors while parsing and ingesting the spreadsheet
     *             into the repository
     */
    @SuppressWarnings("unchecked")
    public void ingestFileLevelSpreadsheet(List<String> collectionsIds,
	    InputStream fileLevelSs) throws FoxmlException {
	float increment = 0;
	String instance = "";
	String rootCollection = "";
	StringBuilder sb = new StringBuilder();
	List<String> mylist = new ArrayList<String>();
	boolean generateQudexXml = true, resultsOK = true, deleteAllDocuments = false, collectionFromApp = false;

	this.progressStatus = "0%";
	resultsOK = getSpreadsheetManager();
	if (!resultsOK)
	    throw new FoxmlException("File is neither CSV or EXCEL...");
	try {
	    // AMG- (1) CREATE MAP STRUCTURE FROM XLS OR CSV FILE
	    try {

		resultsOK = spService.readSpreadsheet(fileLevelSs);
		// AMG Estimated time for reading spreadsheet
		this.progressStatus = "15%";
		if (!resultsOK) {
		    log.fatal("Errors while parsing spreadsheet...");
		    this.quObject.addFedoraResultsMessage(
			    "Error in ingestFileLevelSpreadsheet", "");
		    return;
		}
	    } catch (Exception ex) {
		sb
			.append("In readSpreadsheet: Map structure from spreadsheet not created ");
		sb.append(ex.getMessage());
		log.fatal(sb.toString());
		// AMG- ADD RESULTS ERROR OBJECT
		this.quObject.addFedoraResultsMessage(
			"Error in ingestFileLevelSpreadsheet", ex.getMessage());
		throw new FoxmlException(
			"Map structure from spreadsheet not created");
	    }
	    // AMG- Progress Bar
	    increment = (20 / (float) this.spService.getSpreadsheetMap()
		    .getGlobalMap().size());
	    // AMG - Initialise Qudex Instance XML
	    this.qudexManager.createQudexInstance("other", "fedora-pid");
	    // Init FedoraObject in FoxmlBeans
	    this.foxmlBeans.initFedoraObject(this.quObject.getFedoraObject());
	    int counter = 1;
	    
	    if (collectionsIds.size() >= 1 && !collectionsIds.get(0).equals("")) {
		collectionFromApp = true;
		// Check collection validity
		resultsOK = this.quObject.isCollectionWithQudexInstance(collectionsIds.get(0));
		if (resultsOK) {
		    instance = this.quObject.getCollectionWithQudexInstance(collectionsIds.get(0));
		    if (instance != null) {
			try {
			    this.qudexManager.setQudexInstance(QudexDocument.Factory.parse(instance));
			} catch (XmlException e) {
			    log.error("Error in manager:", e);
			    this.quObject.addFedoraResultsMessage("manager", "Error retrieving Qudex Instance from selected Collection");
			    return;
			} 
		    } else {
			this.quObject.addFedoraResultsMessage("manager", "Error retrieving Qudex Instance from selected Collection");
			return;
		    }
		    //this.quObject.addFedoraResultsMessage(
			//	"manager",
			//	"The selected Qudex Collection already presents Qudex Documents. Specify a different Collection PID");
		    //return;
		    //Create Instance from 
		}
		resultsOK = this.relsManagement.setDocumentsParentCollection(collectionsIds.get(0),
			uk.da.dext.utilities.Constants.FEDORA_IS_PART, this.getFedoraObject());
		if (!resultsOK) {
		    // Collection is not a valid collection, then abort
		    throw new FoxmlException("Container collection with PID: "
			    + collectionsIds.get(0)
			    + " is not a valid collection.");
		} else {
		    // AMG Add here the root collection, if this is a sub-collection
		    String auxCollection = this.relsManagement.getRootCollection(collectionsIds.get(0));
		    if (auxCollection == null) {
			// Collection is not a valid collection, then abort
			this.quObject.addFedoraResultsMessage("manager", "Errors in collection-document relationships. Contact administrator");
			return;
		    }
		    if (!auxCollection.equals(collectionsIds.get(0))) {
			resultsOK = this.relsManagement.setDocumentsParentCollection(auxCollection,
				uk.da.dext.utilities.Constants.FEDORA_IS_PART, this.getFedoraObject());
			rootCollection = auxCollection;
			this.relsManagement.getQudexCollectionsPIDs().add(rootCollection.replace(MetadataUtilities.OBJECTID, ""));
			resultsOK = this.quObject.isCollectionWithQudexInstance(rootCollection);
			if (resultsOK) {
			    instance = this.quObject.getCollectionWithQudexInstance(rootCollection);
			    if (instance != null) {
				try {
				    this.qudexManager.setQudexInstance(QudexDocument.Factory.parse(instance));				    
				} catch (XmlException e) {
				    log.error("Error in manager:", e);
				    this.quObject.addFedoraResultsMessage("manager", "Error retrieving Qudex Instance from selected Collection");
				    return;
				} 
			    } else {
				this.quObject.addFedoraResultsMessage("manager", "Error retrieving Qudex Instance from selected Collection");
				return;
			    }
			}
		    } else {
			this.relsManagement.getQudexCollectionsPIDs().add(collectionsIds.get(0));
		    }
		}
	    }
	    // AMG- EXTERNAL LOOP FOR EACH ROW IN THE MAP STRUCTURE
	    for (ColumnMap item : spService.getSpreadsheetMap().getGlobalMap()
		    .values()) {
		// AMG (16/11) Init FoxmlBeans Document
		this.foxmlBeans.initFoxmlDocumentObject();
		try {
		    // AMG- (2) CREATE EMPTY FOXML OBJECT
		    this.createFoxmlFile(Constants.DEF_QUDEX_NS, "true");
		    String label = RelationshipsManagement.findLabel("title", item,
		    	    this.fileType);
		    sb.append(label != "" ? label : "QuDEx File Object");
		    this.foxmlBeans.addLabelProperty(sb.toString());
		    sb.delete(0, sb.toString().length());
		} catch (FoxmlException ex) {
		    sb.append("Error while creating Foxml instance: ");
		    sb.append(ex.getMessage());
		    log.fatal(sb.toString());
		    this.quObject.addFedoraResultsMessage("manager", sb.toString());
		    return;
		}
		// ADD NEW DOCUMENT STRUCT ELEMENT FOR THIS PID
		DocumentStruct docStruct = new DocumentStruct();
		docStruct.setFedoraDocumentPid(this.foxmlBeans.getFoxmlDoc()
			.getDigitalObject().getPID());
		// AMG - MARCH Add location in a source as Fedora PID not the
		// link to the original document
		docStruct.setDocumentLocation(this.foxmlBeans.getFoxmlDoc()
			.getDigitalObject().getPID());
		this.relsManagement.getDocumentsList().add(docStruct);
		/*
		 * AMG- THE FILE FOXML OBJECT WILL CONTAIN A RELS-EXT AND A DC
		 * DATASTREAM BY DEFAULT PLUS FILE EXTERNAL DS POINTING TO THE
		 * REAL LOCATION OF THE ORIGINAL FILE IT IS REFERRING TO
		 * NAMESPACES MANAGEMENT
		 */
		try {
		    resultsOK = addObjectMetadataElements(item, collectionFromApp);
		} catch (QudexException e) {
		    log.error("Error: ", e);
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans.getFoxmlDoc()
			    .getDigitalObject().getPID(), "Document not ingested, error while adding metadata fields.");
		    sb.delete(0, sb.toString().length());
		    deleteAllDocuments = true;
		    break;
		}

		// AMG (07/12) FOXML NOT ADDED, skip to the next one
		if (!resultsOK) {
		    deleteAllDocuments = true;
		    break;
		}
		/*
		 * AMG (4) If Content Models added, ADD here the code to create
		 * and manage RELS-EXT DS
		 */
		// AMG (5) Foxml Validation. Create an XmlOptions instance and
		// set the error listener.
		XmlOptions validateOptions = new XmlOptions();
		ArrayList<XmlError> errorList = new ArrayList<XmlError>();
		validateOptions.setErrorListener(errorList);
		resultsOK = this.foxmlBeans.getFoxmlDoc().validate(
			validateOptions);
		if (!resultsOK) {
		    for (int i = 0; i < errorList.size(); i++) {
			XmlError error = (XmlError) errorList.get(i);
			sb.append("RESULTS OF VALIDATION \nMessage: ");
			sb.append(error.getMessage());
			sb.append("\n");
			sb.append(error.getCursorLocation().xmlText());
			log.fatal(sb.toString());
			sb.delete(0, sb.toString().length());
		    }
		    log.fatal("Error while validating FOXML instance");
		    this.quObject.addFedoraResultsMessage("",
			    "Error while validating FOXML instance");
		    deleteAllDocuments = true;
		    continue;
		}

		// AMG- (6) CALL TO INGEST METHOD
		sb.append(Constants.XML_VERSION_HEADER);
		sb.append(this.foxmlBeans.getFoxmlDoc().toString());
		String results = this.quObject.ingestObjectFedora("QuDEx Document Object", Constants.DEF_QUDEX_NS, sb
			.toString(), this.foxmlBeans.getFoxmlDoc()
			.getDigitalObject().getPID());
		sb.delete(0, sb.toString().length());

		/*
		 * AMG- (7) Additional error checking is needed. If errors
		 * on any object then the qudex instance should not be
		 * generated?
		 */
		if (!results.startsWith(Constants.DEF_QUDEX_NS)) {
		    generateQudexXml = false;
		    // ERROR MANAGEMENT, remove this PID from Document Elements
		    this.relsManagement.removeDocumentElementFromList(this.foxmlBeans
			    .getFoxmlDoc().getDigitalObject().getPID());
		    // Try with the next row/document object
		    // RESULTS OBJECT CREATION AND PARSE OF INGESTION RESULTS
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans.getFoxmlDoc()
			    .getDigitalObject().getPID(), results);
		    deleteAllDocuments = true;
		    break;
		} else {
		    mylist.add(this.foxmlBeans.getFoxmlDoc().getDigitalObject()
			    .getPID());
		}
		// AMG - Add relationships
		resultsOK = this.relsManagement.generateDocumentRelationships(this.foxmlBeans.getFoxmlDoc().getDigitalObject().getPID(), 
			this.getFedoraObject());
		if (!resultsOK) {
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
			    .getFoxmlDoc().getDigitalObject().getPID(),
			    "Errors while creating document-collection relationship information. Contact administrator.");
		}
		// Regenerate Collection-documents relationhsip information
		if (collectionFromApp) {
		    this.relsManagement.setDocumentsParentCollection(collectionsIds.get(0), uk.da.dext.utilities.Constants.FEDORA_IS_PART, 
			    this.getFedoraObject());
		    if (rootCollection != "")
			this.relsManagement.setDocumentsParentCollection(rootCollection, uk.da.dext.utilities.Constants.FEDORA_IS_PART, 
			    this.getFedoraObject());
		}
		// AMG - (8) Ingest Files datastreams
		try {
		    //ingestDatastreams(item);
		    resultsOK = ingestDatastreamsDocument(item);
		    if (!resultsOK) {
			this.quObject.addFedoraResultsMessage(this.foxmlBeans
				    .getFoxmlDoc().getDigitalObject().getPID(),
				    "Errors storing qudex original document. Contact administrator.");
			deleteAllDocuments = true;
			break;			
		    }
		} catch (Exception ex) {
		    // RESULTS OBJECT CREATION AND PARSE OF INGESTION RESULTS
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
				    .getFoxmlDoc().getDigitalObject().getPID(),
				    "Errors storing qudex original document. Contact administrator.");
		    deleteAllDocuments = true;
		    break;
		}
		// AMG - Initialise the document XML ID and assign it to the
		// memos, codes and categories managers
		initXmlManagerIDS();
		// AMG - (9) CATEGORY MANAGEMENT
		resultsOK = generateCategoryStructure(item);
		if (!resultsOK) {
		    log.fatal("Categories not generated for object: "
			    + this.foxmlBeans.getFoxmlDoc().getDigitalObject()
				    .getPID());
		    // RESULTS OBJECT CREATION AND PARSE OF INGESTION RESULTS
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
				    .getFoxmlDoc().getDigitalObject().getPID(),
				    "Errors creating categories for the document. Check spreadsheet.");
		    deleteAllDocuments = true;
		    continue;
		}
		// AMG - (10) CODE MANAGEMENT
		resultsOK = generateCodeStructure(item);
		if (!resultsOK) {
		    log.fatal("Codes not generated for object: "
			    + this.foxmlBeans.getFoxmlDoc().getDigitalObject()
				    .getPID());
		    // RESULTS OBJECT CREATION AND PARSE OF INGESTION RESULTS
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
				    .getFoxmlDoc().getDigitalObject().getPID(),
				    "Errors creating codes for the document. Check spreadsheet.");
		    deleteAllDocuments = true;
		    continue;
		}

		// AMG - (11) MEMO MANAGEMENT
		resultsOK = generateMemoStructure(item);
		if (!resultsOK) {
		    log.fatal("Memos not generated for object: "
			    + this.foxmlBeans.getFoxmlDoc().getDigitalObject()
				    .getPID());
		    // RESULTS OBJECT CREATION AND PARSE OF INGESTION RESULTS
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
				    .getFoxmlDoc().getDigitalObject().getPID(),
				    "Errors creating memos for the document. Check spreadsheet.");
		    deleteAllDocuments = true;
		    continue;
		}
		
		if (!deleteAllDocuments) {
		    this.quObject.addFedoraResultsMessage(this.foxmlBeans
			.getFoxmlDoc().getDigitalObject().getPID(),
			"File stored in the repository -- PID: "
				+ this.foxmlBeans.getFoxmlDoc()
					.getDigitalObject().getPID() + ". Title of the File: " + this.foxmlBeans.getFoxmlLabel());
		}
		// AMG- Progress bar update
		int auxInt = (int) Math.round((float) increment*counter + 15);
		this.progressStatus = Integer.toString(auxInt) + "%";
		counter++;
	    }// END FOR
	    
	    // AMG- Generate Qudex XML Instance
	    if (generateQudexXml && !deleteAllDocuments) {
		// AMG - (14) Generate Fedora Level MEMOS
		if (this.isMemosPresent) {
		    counter = 1;
		    increment = (15 / this.qudexManager.getMemoManager().getElementsList().size());
		    for (MemoStruct memo : (List<MemoStruct>) this.qudexManager.getMemoManager().getElementsList()) {
			resultsOK = this.quObject.generateMemos(memo, this.qudexManager, rootCollection);
			if (!resultsOK) {
			    deleteAllDocuments = true;
			    this.progressStatus = "95%";
			    this.quObject.addFedoraResultsMessage(this.foxmlBeans
					.getFoxmlDoc().getDigitalObject()
					.getPID(),
					"Errors creating memos for the document with title: " + this.foxmlBeans.getFoxmlLabel() + ". Check spreadsheet.");
			    break;
			}
			int auxInt = (int) Math.round((float) increment*counter + 35);
			this.progressStatus = Integer.toString(auxInt) + "%";
			counter++;
		    }
		}
		if (this.isCodesPresent && !deleteAllDocuments) {
		    counter = 1;
		    increment = (10 / this.qudexManager.getCodeManager().getElementsList().size());
		    for (CodeStruct code : (List<CodeStruct>) this.qudexManager.getCodeManager().getElementsList()) {
			resultsOK = this.quObject.generateCodes(code, this.qudexManager, rootCollection);
			if (!resultsOK) {
			    this.quObject.addFedoraResultsMessage(this.foxmlBeans
					.getFoxmlDoc().getDigitalObject()
					.getPID(),
					"Errors creating codes for the document with title: " + this.foxmlBeans.getFoxmlLabel() + ". Check spreadsheet.");
			    this.progressStatus = "95%";
			    deleteAllDocuments = true;
			    break;
			}
			int auxInt = (int) Math.round((float) increment*counter + 50);
			this.progressStatus = Integer.toString(auxInt) + "%";
			counter++;
		    }
		}
		// AMG - (12) Create FEDORA categories object
		if (this.isCategoriesPresent && !deleteAllDocuments) {
		    //counter = 1;
		    //increment = (40 / mylist.size());
		    //for (String pid: mylist) {
			//resultsOK = this.quObject.createCategoryDatastreams(pid, this.qudexManager);
			//if (!resultsOK) {
			//    this.quObject.deleteAllFedoraDocuments(mylist);
			//    this.quObject.addFedoraResultsMessage("manager",
			//		"Fedora Collection generated errors in documents, therefore process ABORTED.");
			//}
			//this.progressStatus = Long.toString(increment*counter + 50) + "%";
			//counter++;
		    //}		   
		    counter = 1;
		    increment = (35 / (float) this.qudexManager.getCategoryManager().getElementsList().size());
		    // AMG SEPT 2013 - MARK existing categories
		    if (instance != "")
				checkExistingCategories(instance, rootCollection);
		    for (CategoryStruct category : (List<CategoryStruct>) this.qudexManager.getCategoryManager().getElementsList()) {
        		    resultsOK = this.quObject.generateCategories(category, this.qudexManager, rootCollection);		    
        		    if (!resultsOK) {
        			this.progressStatus = "95%";
        			deleteAllDocuments = true;
        			break;
        		    }
        		    int auxInt = (int) Math.round((float) increment*counter + 60);
        		    this.progressStatus = Integer.toString(auxInt) + "%";
        		    counter++;
		    }
		}
		if (deleteAllDocuments) {
		    //Delete all categories
		    if (this.isCategoriesPresent)
			this.quObject.deleteAllCategories();
		    if (this.isCodesPresent)
			this.quObject.deleteAllCodes();
		    if (this.isMemosPresent)
			this.quObject.deleteAllMemos();
		    this.quObject.deleteAllFedoraDocuments(mylist);
		    this.quObject.addFedoraResultsMessage("manager",
			"Fedora Collection generated errors in documents, therefore process ABORTED.");
		} else {
		    // AMG - (15.0) Add relationships to Qudex Instance
		    this.qudexManager.createObjectsRelationships();
		    // AMG (15.1) Generate documents
		    qudexManager.generateQudexDocuments(this.qudexManager
			.getCategoryManager(), this.relsManagement.getDocumentsList());
		    // AMG (15.2) Validate Qudex Instance
		    resultsOK = qudexManager.validateQudexInstance();
		    if (resultsOK) {
			// AMG (15.2.1) Add Qudex Instance Datastream to the Qudex
			// Collection Object
			resultsOK = this.quObject.addQudexInstanceToFedoraCollection(this.foxmlBeans, 
				this.relsManagement.getQudexCollectionsPIDs(), 
			    this.qudexManager.getQudexInstance());
			if (!resultsOK) {
			    this.quObject.deleteAllFedoraDocuments(mylist);
			    this.quObject.addFedoraResultsMessage("manager",
					"Qudex Instance not added to collection object. Contact the administrator.");
			}
			// AMG - ADD SPREADSHEET DS
			resultsOK = this.quObject.addSpreadsheetToObject("FILES_SP" + UUID.randomUUID().toString(), 
				this.relsManagement.getQudexCollectionsPIDs().get(0), this.fileType);
			if (!resultsOK) {
			    this.quObject.addFedoraResultsMessage(this.foxmlBeans
				    .getFoxmlDoc().getDigitalObject().getPID(),
				    "Errors while adding Ingestion Spreadsheet to collection object.");
			}
		    }
		}
	    } else if (deleteAllDocuments) {
		// ABORT
		this.quObject.deleteAllFedoraDocuments(mylist);
		this.quObject.addFedoraResultsMessage("manager",
				"Fedora Collection generated errors in documents, therefore process ABORTED.");
	    }
	    if (!this.progressStatus.equals("100%"))
		this.progressStatus = "100%";
	    // AMG- Give time to AJAX calls to update when using progress bars
	    // in the user interface
	    Thread.sleep(2000);
	} catch (InterruptedException e) {
	    log.fatal(e.getMessage());
	} catch (JFigException e) {
	    log.fatal(e.getMessage());
	    this.quObject.addFedoraResultsMessage("manager", "Errors in the application, contact administrator.");
	} finally {
	    // Clean instances of maps
	    this.cleanInstance();
	}
    }

    /**
     * 
     * @param item
     *            actual item being inspected from the row contents of the
     *            spreadsheet
     * @param collFromApp
     * 		  boolean that specifies whether to parse relationship information
     * @return true if metadata elements added, false otherwise
     * @throws QudexException
     *             , FoxmlException
     */
    private boolean addObjectMetadataElements(ColumnMap item, boolean collFromApp)
	    throws FoxmlException, QudexException {
	List<String> namespacesList = new ArrayList<String>();
	QudexFoxmlValidator qudexValidator = null;
	Iterator<SchemaInstance> it = this.spService
		.getSchemaInstanceContainer().getListSchemaInstances()
		.iterator();
	boolean DC = false, schemaFound = false, valid = true, resultsOK, relsInfoPresent = false;
	StringBuilder sb = new StringBuilder();
	String namespacePrefix = "";

	// AMG- Create empty document
	Document docDC = DocumentHelper.createDocument();
	Document doc = SchemasProcessing.contructRDFDocument(this.foxmlBeans
		.getFoxmlDoc().getDigitalObject().getPID(),
		new ArrayList<String>());

	try {
	    // AMG- Create Qudex validator
	    qudexValidator = new QudexFoxmlValidatorImpl(
		    this.isQudexCollections);

	    while (it.hasNext()) {
		DC = false;

		SchemaInstance elem = (SchemaInstance) it.next();
		// Very important preserve namespaces
		ValueType columnPrefix = MetadataUtilities.MDSchemas.get(elem
			.getSchemaPrefix());
		if (MetadataUtilities.MDSchemas.get(elem.getSchemaPrefix()
			.toLowerCase()) == MetadataUtilities.ValueType.DcElement) {
		    // DC is not RDF, is special oai_dc XML
		    DC = true;
		    docDC = DcUtilities.constructDCEmptyDocument();
		} else {
			namespacePrefix = elem.getSchemaPrefix().toLowerCase();
		    SchemasProcessing.addNamespacesToDocument(doc, namespacePrefix);
		    if (doc == null) {
			return false;
		    }
		    // Add datastream name
		    if (!schemaFound) {
			schemaFound = true;
		    }
		    namespacesList.add(elem.getSchemaPrefix().toLowerCase());
		}

		for (SchemaElem schemaElem : elem.getSchemaElemsList()) {
		    String property, value = "";
		    for (Column col : ((ColumnMap) item).getColumnMap()
			    .keySet()) {
			if (columnPrefix.equals(col.getColumnType())
				&& col.getColumnName().equalsIgnoreCase(
					schemaElem.getElementName())) {
			    // Qualified property
			    sb.append(elem.getSchemaPrefix());
			    sb.append(Constants.PID_DELIMITER);
			    sb.append(col.getColumnName());
			    property = sb.toString();
			    sb.delete(0, sb.toString().length());
			    String results = "";
			    try {
				results = spService
					.returnStringCellContent(((ColumnMap) item)
						.getColumnMap().get(col));
				value = results;
			    } catch (Exception ex) {
				if (ex.getMessage() != "")
				    log.fatal(ex.getMessage());
				else
				    log.error("Error: ", ex);

				return false;
			    }
			    if (DC) {
				if (value.contains(Constants.FIELDS_SEPARATOR)) {
				    String[] arrayValues = value
					    .split(Constants.FIELDS_SEPARATOR);
				    for (String str : arrayValues) {
					resultsOK = DcUtilities
						.addOAIDCproperty(property,
							str, docDC);
					if (!resultsOK) {
					    log.error("DC property not added: "
						    + property);
					}
				    }
				} else {
				    // AMG - Add qudex:isOriginal value from ss
				    // into the documentstruct object associated
				    // with this document
				    addDocumentDCFields(property, value);
				    resultsOK = DcUtilities.addOAIDCproperty(
					    property, value, docDC);
				    if (!resultsOK) {
					log.error("DC property not added: "
						+ property);
				    }
				}
			    } else {
				// AMG - Normal RDF
				if (value.contains(Constants.FIELDS_SEPARATOR)) {
				    String[] arrayValues = value
					    .split(Constants.FIELDS_SEPARATOR);
				    for (String str : arrayValues) {
					// AMG Check if property is collection
					// membership
					if (RelationshipsManagement
						.isRelationshipProperty(property)) {
					    if (!collFromApp) {
						// AMG ERROR, WE CANT SPECIFY MULTIPLE COLLECTIONS HERE
						this.quObject.addFedoraResultsMessage("manager", "Error in spreadsheet: Relationships field in Spreadsheet contains multiple values for a cell. Only one value per cell is needed.");
						return false;
					    } else {
						relsInfoPresent = true;
						break;
					    }
					} else {
					    resultsOK = SchemasProcessing
						    .addRDFProperty(
							    namespacePrefix,
							    property,
							    str,
							    schemaElem
								    .getPropertyValueType(),
							    doc);
					    if (!resultsOK) {
						continue;
					    }
					}
				    }
				} else {
				    // AMG - Add qudex:isOriginal value from ss
				    // into the documentstruct object associated
				    // with this document
				    addDocumentDCFields(property, value);
				    // The property has to come qualified, e.g.
				    // rel:isMemberOf
				    if (RelationshipsManagement
					    .isRelationshipProperty(property)) {
					// We add all the membership properties
					// before ingesting the datastream
					// THIS relationship will be stored as
					// property before creating the DS not
					// now
					if (!collFromApp) {
					    resultsOK = this.relsManagement.checkCollectionRelationshipValidity(this.isQudexCollections, property, value, this.quObject);
					    if (!resultsOK) {
						this.quObject.addFedoraResultsMessage("manager", "Collection relationship in spreadsheet not valid. Check Spreadsheet.");
						return false;
					    }
					    relsInfoPresent = true;
					} else relsInfoPresent = true;
				    } else {
					resultsOK = SchemasProcessing
						.addRDFProperty(
							namespacePrefix,
							property,
							value,
							schemaElem
								.getPropertyValueType(),
							doc);
					if (!resultsOK) {
					    continue;
					}
				    }
				}
			    }
			}
		    }
		}
		if (DC) {
		    try {
			List<String> dcNs = new ArrayList<String>();
			dcNs.add("dc");

			valid = qudexValidator.validateQudexElements(docDC
				.asXML(), dcNs, this.quObject);
			if (valid)
			    this.foxmlBeans.addDublinCoreDatastream(docDC,
				    Constants.DEFAULT_MIMETYPE);
			else {
			    this.quObject.addFedoraResultsMessage("manager", "Check spreadsheet Dublin Core headers. Validation against Spreadsheet Schema failed.");
			    return false;
			}
		    } catch (FoxmlException ex) {
			log.fatal(ex.getMessage());
			return false;
		    }
		    DC = false;
		}
	    }
	    if (schemaFound) {
		try {
		    sb.append("RELS-EXT record for the object");
		    // AMG Check if rdf:type collection is present
		    if (this.isQudexCollections) {
			Element desc = doc.getRootElement().element(
				"rdf:Description");
			boolean typePresent = SchemasProcessing
				.checkPropertyExistence(desc, "rdf:type",
					MetadataUtilities.DCMI_ELEMENTS
						.get("collection"));
			if (!typePresent) {
			    resultsOK = SchemasProcessing.addRDFProperty("rdf",
				    "rdf:type", MetadataUtilities.DCMI_ELEMENTS
					    .get("collection"),
				    MetadataUtilities.propertyTypes.Resource
					    .toString(), doc);
			    if (!resultsOK) {
				this.quObject.addFedoraResultsMessage("manager", "Errors in Spreadsheet. Collection Type missing. Check Spreadsheet");
				return false;
			    }
			}
		    }
		    // ONLY FOR FILE SPREADSHEETS		    
		    if (!collFromApp && !relsInfoPresent && !this.isQudexCollections) {
			this.quObject.addFedoraResultsMessage("manager", "No relationship information has been specified. Either fill the rel:isPartOf field from the spreadsheet or select a Collection from the Web Manager.");
			return false;
		    }
		    valid = qudexValidator.validateQudexElements(doc.asXML(),
			    namespacesList, this.quObject);
		    if (valid) {
			// AMG- For Qudex, non DC elements go into RELS-EXT DS,
			// with subject the OBJECT PID
			this.foxmlBeans.addDatastream(
				Constants.DEFAULT_RELSEXT_DS_NAME,
				DatastreamType.CONTROLGROUP.X, sb.toString(),
				StateType.A, doc.asXML(),
				Constants.DEFAULT_MIMETYPE, false,
				false);/* isVersionable */
			sb.delete(0, sb.toString().length());
		    } else {
			this.quObject.addFedoraResultsMessage("manager", "Check spreadsheet headers. Validation against Spreadsheet Schema failed.");
			return false;
		    }
		} catch (FoxmlException ex) {
		    log.error("Errors in addMetadataElements: ", ex);
		    this.quObject.addFedoraResultsMessage("manager", "Errors occured while parsing spreadsheet headers. Contact administrator.");
		    return false;
		}
	    }
	    return true;
	} catch (Exception ex) {
	    log.fatal(ex.getMessage());
	    this.quObject.addFedoraResultsMessage("", "Error while constructing metadata records. Consult administrator.");
	    return false;
	} finally {
	    qudexValidator.cleanValidator();
	}
    }

    /**
     * Clean all instances of this instance but communications objects
     */
    public void cleanInstance() {
	this.spService.cleanInstance();
	this.isCategoriesPresent = false;
	this.isCodesPresent = false;
	this.isMemosPresent = false;
	this.foxmlBeans.clearObjects();
	this.progressStatus = "0%";
	this.relsManagement.cleanInstance();
	// Clear RelationshipManager
	this.qudexManager.cleanManager();
	this.quObject.cleanQudexFedoraObjects();
    }

    /**
     * Clean all instances of this instance but communications objects
     */
    public void cleanFullInstance() {
	if (this.spService != null)
	    this.spService.cleanInstance();
	if (this.foxmlBeans != null)
	    this.foxmlBeans.clearObjects();
	// Clear the qudexManager Objects
	if (this.qudexManager != null) {
	    this.qudexManager.cleanManager();
	}
	this.relsManagement.cleanInstance();
	// Clear communication objects
	if (this.quObject.getFedoraObject() != null)
	    this.quObject.setFedoraObject(null);
    }
    /**
     * Return an instance of the Fedora Operations Object
     * @return FedoraOperations Object
     */
    public FedoraOperationsAPIM getFedoraObject() {
	return this.quObject.getFedoraObject();
    }
    // PRIVATE METHODS
    // MANAGE DATASTREAMS
    /**
     * Ingest associated DataStreams to a FOXML Digital object created already
     * from the spreadsheet
     * 
     * @param option
     *            ingestion: External contents
     * @param item
     *            particular row object from the spreadsheet
     * @param foxml
     *            FOXML document current instance
     * @return true if everything OK, false otherwise
     * @throws FoxmlException
     */
    private boolean ingestDatastreamsDocument(ColumnMap item)
	    throws FoxmlException {
	boolean ingested = false;
	// AMG - Find "qudex:isOriginal" column, if value is TRUE then we are looking for "sourceReference"
	//otherwise looking for "originalReference"
	boolean isOriginal = this.getIsOriginalValue(item);
	String fieldToSearch = "";
	if (isOriginal)
	    fieldToSearch = Constants.QUDEX_SOURCE_REFERENCE;
	else fieldToSearch = Constants.QUDEX_ORIGINAL_REFERENCE;
	// JUST EXTERNAL DSs
	for (Column col : item.getColumnMap().keySet()) {

	    // Get original File column
	    if (col.getColumnName().equalsIgnoreCase(
		    fieldToSearch)) {
		String cellValue = spService.getCellContent(item, col);
		// Commented by NOW since what goes in location from a source
		// element is the fedora document PID
		// addDocumentLocationField(col.getColumnName(), cellValue);
		if (col.getColumnType() == MetadataUtilities.ValueType.URL
			//TODO AMG changes this to use Redirect DS instead of External
			|| col.getColumnType() == MetadataUtilities.ValueType.R) {
		    // Must be External reference
		    boolean val = ingestExternalDS(cellValue, col);
		    if (val)
			ingested = true;
		} else if (col.getColumnType() == MetadataUtilities.ValueType.M) {
		    // Just in case the original file is stored in the
		    // repository
		    String mimeType = "";
		    int startMimetype = cellValue.indexOf("&type");
		    if (startMimetype != -1) {
			String fullMimetype = cellValue.substring(startMimetype);
			int startIdx = fullMimetype.indexOf("=");
			mimeType = fullMimetype.substring(startIdx + 1);
		    }
		    int colon = col.getColumnName().indexOf(':');
		    String dsName = colon >= 0 ? col.getColumnName().substring(
			    0, colon).trim() : col.getColumnName();
		    try {
			if (mimeType.equals("")) {
			    log.info("MimeType Empty");
			    mimeType = spService.getMimeType(2, cellValue);
			}
		    } catch (IOException e) {
			log.fatal(e.getMessage());
			mimeType = "";
		    }
		    foxmlBeans.addDatastream(dsName,
			    DatastreamType.CONTROLGROUP.M, "Managed content",
			    StateType.A, cellValue, mimeType, true, 
			    true); /* isVersionable */
		    ingested = true;
		}
		break; //item found so no need to look for other datastreams
	    }
	}// END FOR
	
	return ingested;
    }

    /**
     * Sends a Fedora API-M request to create a new external datastream
     * associated with a particular FOXML object already present in the
     * repository
     * 
     * @param value
     *            datastream name
     * @param col
     *            data associated with the datastream comming from the
     *            Spreadsheet MAP structure
     * @return boolean true if the request was performed OK, false otherwise
     * @throws FoxmlException
     *             any error in the parsing library
     */
    private boolean ingestExternalDS(String value, Column col)
	    throws FoxmlException {
	boolean val = SchemasProcessing.validateURL(value);
	String mimetype;
	try {
	    mimetype = spService.getMimeType(2, value);
	} catch (IOException e1) {
	    log.fatal(e1.getMessage());
	    return false;
	}
	if (mimetype == null)
	    mimetype = "";
	if (!val) {
	    log.info("Object PID: "
		    + this.foxmlBeans.getFoxmlDoc().getDigitalObject().getPID()
		    + "   Invalid External datastream URL, " + value);
	    return false;
	} else {
	    int colon = col.getColumnName().indexOf(':');
	    String dsName = colon >= 0 ? col.getColumnName()
		    .substring(0, colon).trim() : col.getColumnName();
	    try {
		// ADD DATASTREAM
		this.foxmlBeans.addDatastream(dsName, /* Datastream name */
		//TODO AMG changed USE Redirect instead of External DS
		DatastreamType.CONTROLGROUP.R, /* Type of DS */
		"Externally referenced content", /* Label for the DS */
		StateType.A, /* DS status, Active */
		value, /* Contents */
		mimetype, /* Mimetype */
		true, /* Send request via HTTP */
		true); /* isVersionable */
	    } catch (FoxmlException e) {
		log.fatal(e.getMessage());
		throw e;
	    }
	}
	// Everything went OK
	return true;
    }

    // MANAGE CATEGORIES, CODES, MEMOS AT FEDORA LEVEL. QUDEX LEVEL IS MANAGED
    // IN THE QUDEX BEANS LIBRARY

    private void initXmlManagerIDS() {
	// Generate Current Document XML ID
	String id = QudexManagement.generateID();
	// Assign the associated Foxml Document PID to the Memos manager
	this.qudexManager.getMemoManager().setCurrentDocumentPID(
		this.foxmlBeans.getFoxmlDoc().getDigitalObject().getPID());
	// Assign the current document XML ID to the Memos manager
	this.qudexManager.getMemoManager().setCurrentDocumentXmlId(id);
	// Assign the associated Foxml Document PID to the Codes manager
	this.qudexManager.getCodeManager().setCurrentDocumentPID(
		this.foxmlBeans.getFoxmlDoc().getDigitalObject().getPID());
	// Assign the current document XML ID to the Codes manager
	this.qudexManager.getCodeManager().setCurrentDocumentXmlId(id);
	// Assign the associated Foxml Document PID to the Categories manager
	this.qudexManager.getCategoryManager().setCurrentDocumentPID(
		this.foxmlBeans.getFoxmlDoc().getDigitalObject().getPID());
	// Assign the current document XML ID to the Categories manager
	this.qudexManager.getCategoryManager().setCurrentDocumentXmlId(id);
    }

    /**
     * Parses the current spreadsheet row to look for category values and then
     * adds them to the categoryManagement structure to generate the associated
     * category Fedora Object
     * 
     * @param item
     *            current parsed spreadsheet document row
     * @return true if the process went OK, false if any error found
     */
    private boolean generateCategoryStructure(ColumnMap item) {
	String valueCat = "", labelCat = "";
	String catLanguage = "";
	// In the whole document map find only value_category and label_category
	// headers
	for (Column col : item.getColumnMap().keySet()) {
	    if (col.getColumnName().equalsIgnoreCase(
		    Constants.CATEGORY_VALUE_HEADER)) {
		valueCat = item.getColumnMap().get(col).toString();
	    } else if (col.getColumnName().equalsIgnoreCase(
		    Constants.CATEGORY_LABEL_HEADER)) {
		labelCat = item.getColumnMap().get(col).toString();
	    } else if (col.getColumnName().equalsIgnoreCase(
		    Constants.DC_LANGUAGE_UNPREFFIXED)) {
		catLanguage = item.getColumnMap().get(col).toString();
	    }
	}
	if (valueCat.equals("") && labelCat.equals("")) {
	    //this.isCategoriesPresent = false;
	    return true;
	} else if (valueCat.equals("") || labelCat.equals(""))
	    // Error in spreadsheet data, categories for this object not added
	    return false;
	// Generate map with pairs label-display-label for each category
	boolean results = this.qudexManager.getCategoryManager().generateElementsMap(valueCat, labelCat);
	if (!results) 
	    return false;
	this.qudexManager.getCategoryManager().constructListOfElements(catLanguage);
	if (!this.isCategoriesPresent)
	    this.isCategoriesPresent = true;
	return true;
    }

    // CODES MANAGEMENT
    /**
     * Parses the current spreadsheet row to look for category values and then
     * adds them to the categoryManagement structure to generate the associated
     * code Fedora Object
     * 
     * @param item
     *            current parsed spreadsheet document row
     * @return true if the process went OK, false if any error found
     */
    private boolean generateCodeStructure(ColumnMap item) {
	String valueCode = "", labelCode = "";
	String language = "";
	// In the whole document map find only value_category and label_category
	// headers
	for (Column col : item.getColumnMap().keySet()) {
	    if (col.getColumnName().equalsIgnoreCase(
		    Constants.CODE_VALUE_HEADER)) {
		valueCode = item.getColumnMap().get(col).toString();
	    } else if (col.getColumnName().equalsIgnoreCase(
		    Constants.CODE_LABEL_HEADER)) {
		labelCode = item.getColumnMap().get(col).toString();
	    } else if (col.getColumnName().equalsIgnoreCase(
		    Constants.DC_LANGUAGE_UNPREFFIXED)) {
		language = item.getColumnMap().get(col).toString();
	    }
	}
	if (valueCode.equals("") && labelCode.equals("")) {
	    //this.isCodesPresent = false;
	    return true;
	} else if (valueCode.equals("") || labelCode.equals(""))
	    return false;
	
	// Generate map with pairs label-display-label for each category
	boolean results = this.qudexManager.getCodeManager().generateElementsMap(valueCode, labelCode);
	if (!results)
	    return false;
	this.qudexManager.getCodeManager().constructListOfElements(language);
	if (!this.isCodesPresent)
	    this.isCodesPresent = true;
	return true;
    }

    // MEMO MANAGEMENT IN THE REPOSITORY
    /**
     * Parses the current spreadsheet row to look for memo values and then adds
     * them to the memoManagement structure to generate the associated code
     * Fedora Object
     * 
     * @param item
     *            current parsed spreadsheet document row
     * @return true if the process went OK, false if any error found
     */
    private boolean generateMemoStructure(ColumnMap item) {
	String valueMemo = "", labelMemo = "";
	String language = "";
	// In the whole document map find only value_category and label_category
	// headers
	for (Column col : item.getColumnMap().keySet()) {
	    if (col.getColumnName().equalsIgnoreCase(
		    Constants.MEMO_VALUE_HEADER)) {
		valueMemo = item.getColumnMap().get(col).toString();
	    } else if (col.getColumnName().equalsIgnoreCase(
		    Constants.MEMO_LABEL_HEADER)) {
		labelMemo = item.getColumnMap().get(col).toString();
	    } else if (col.getColumnName().equalsIgnoreCase(
		    Constants.DC_LANGUAGE_UNPREFFIXED)) {
		language = item.getColumnMap().get(col).toString();
	    }
	}
	if (valueMemo.equals("") && labelMemo.equals("")) {
	    //this.isMemosPresent = false;
	    return true;
	} else if (valueMemo.equals("") || labelMemo.equals(""))
	    return false;
	
	// Generate map with pairs label-display-label for each category
	boolean results = this.qudexManager.getMemoManager().generateElementsMap(valueMemo, labelMemo);
	if (!results)
	    return false;
	this.qudexManager.getMemoManager().constructListOfElements(language);
	if (!this.isMemosPresent)
	    this.isMemosPresent = true;
	return true;
    }

    // PRIVATE METHODS TO WORK WITH THE DOCUMENTS ARRAY
    /**
     * 
     * @param docS
     * @param property
     * @param value
     * @return
     */
    private void addDocumentDCFields(String property, String value) {
	DocumentStruct auxDocS = searchDocumentElement(this.foxmlBeans
		.getFoxmlDoc().getDigitalObject().getPID());
	if (auxDocS != null) {
	    if (property.equalsIgnoreCase(Constants.DC_FORMAT)) {
		auxDocS.setDocumentMimeType(value);
	    } else if (property.equalsIgnoreCase(Constants.DC_TYPE))
		auxDocS.setResourceType(value);
	    else if (property.equalsIgnoreCase(Constants.QUDEX_ORIGINAL))
		auxDocS
			.setOriginalDocument(value.equals("true") ? true
				: false);
	    else if (property.equalsIgnoreCase(Constants.DC_CREATOR))
		auxDocS.setDocumentCreator(value);
	    else if (property.equalsIgnoreCase(Constants.DC_LANGUAGE))
		auxDocS.setDocumentLanguage(value);
	}
    }

    /**
     * Searchs for the reference to the original document in the spreadsheet
     * fields (originalReference:E) and set this value for the field 'location'
     * within a qudex source element
     * 
     * @param property
     *            property to find
     * @param value
     *            value of the property
     */
    @SuppressWarnings("unused")
    private void addDocumentLocationField(String property, String value) {
	DocumentStruct auxDocS = searchDocumentElement(this.foxmlBeans
		.getFoxmlDoc().getDigitalObject().getPID());
	if (auxDocS != null) {
	    if (property.equalsIgnoreCase(Constants.QUDEX_ORIGINAL_REFERENCE))
		auxDocS.setDocumentLocation(value);
	}
    }

    /**
     * Return a documentStruct instance from the array of documents by PID
     * 
     * @param pid
     *            identifier of the document to retrieve
     * @return document if found, null otherwise
     */
    private DocumentStruct searchDocumentElement(String pid) {
	for (DocumentStruct docS : this.relsManagement.getDocumentsList()) {
	    if (docS.getFedoraDocumentPid().equals(pid)) {
		return docS;
	    }
	}
	return null;
    }

   

    private boolean getSpreadsheetManager() {
	// AMG- CHECK fileType
	this.spService = SpreadsheetUtilities.getSpreadsheetManager(fileType,
		true, this.isQudexCollections);
	if (this.spService == null) {
	    log.fatal("File is neither a CSV file or Excel file...");
	    return false;
	}
	return true;
    }
    
//Categories, codes and memos presence
//    private void findCategoriesCodesMemos(ColumnMap item) {
//	Iterator<Column> colIt = item.getColumnMap().keySet().iterator();
//	
//	while (colIt.hasNext()) {
//	    Column col = colIt.next();
//	    if (col.getColumnName().equalsIgnoreCase(Constants.CATEGORY_LABEL_HEADER) ||
//		    col.getColumnName().equalsIgnoreCase(Constants.CATEGORY_VALUE_HEADER))
//		this.isCategoriesPresent = true;
//	    else if (col.getColumnName().equalsIgnoreCase(Constants.CODE_LABEL_HEADER) ||
//		    col.getColumnName().equalsIgnoreCase(Constants.CODE_VALUE_HEADER))
//		this.isCodesPresent = true;
//	    else if (col.getColumnName().equalsIgnoreCase(Constants.MEMO_LABEL_HEADER) ||
//		    col.getColumnName().equalsIgnoreCase(Constants.MEMO_VALUE_HEADER))
//		this.isMemosPresent = true;
//	    
//	    if(isCategoriesPresent && isCodesPresent && isMemosPresent)
//		break;
//	}
//    }
    
    //UPDATE AND DELETE OPERATIONS METHODS
    private String findCollectionPid(String rowIdentifier) {
	return (String) this.spService.getRowColumnValue(rowIdentifier, Constants.PID_LABEL);
    }

    private boolean isUpdatePid(ColumnMap item) {
	boolean isUpdate = false;
	
	for (Column col : item.getColumnMap().keySet()) {
	    if (col.getColumnName()
		    .equalsIgnoreCase(Constants.QUDEX_UPDATE_PID)) {
		isUpdate = Boolean.valueOf(this.spService
			.returnStringCellContent(item.getColumnMap().get(col)));

		return isUpdate;
	    }
	}
	return false;
    }

    private boolean isDeleteOperation(ColumnMap item) {
	boolean isDelete = false;
	for (Column col : item.getColumnMap().keySet()) {
	    if (col.getColumnName()
		    .equalsIgnoreCase(Constants.QUDEX_DELETE_PID)) {
		isDelete = Boolean.valueOf(this.spService
			.returnStringCellContent(item.getColumnMap().get(col)));

		return isDelete;
	    }
	}
	return false;
    }
    
    public List<String> getQudexCollections(String collectionId) {
	try {
	    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
	    return mulgService.retrieveQudexCollections(Constants.QUDEX_PREFIX, collectionId);
	} catch (JFigException e) {
	    log.error("qudexManager", e);
	    return null;
	}
    }
    
    public List<CollectionElement> getQudexCollectionsTree() {
	try {
	    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
	    return mulgService.constructCollectionsTree(Constants.QUDEX_PREFIX);
	} catch (JFigException e) {
	    log.error("qudexManager", e);
	    return null;
	}
    }
    
    public String getQudexRootParent(String collectionId) {
	try {
	    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
	    return mulgService.getCollectionRootParent(collectionId, Constants.QUDEX_PREFIX);
	} catch (JFigException e) {
	    log.error("qudexManager", e);
	    return null;
	}
    }
    /**
     * @param originalSpreadsheetName the originalSpreadsheetName to set
     */
    public void setOriginalSpreadsheetName(String originalSpreadsheetName) {
	this.originalSpreadsheetName = originalSpreadsheetName;
    }

    /**
     * @return the originalSpreadsheetName
     */
    public String getOriginalSpreadsheetName() {
	return originalSpreadsheetName;
    }
    
    private boolean getIsOriginalValue(ColumnMap item) {
	boolean isOriginal = false;
	for (Column col : item.getColumnMap().keySet()) {
	    if (col.getColumnName()
		    .equalsIgnoreCase(Constants.QUDEX_ORIGINAL_NONS)) {
		isOriginal = Boolean.valueOf(this.spService
			.returnStringCellContent(item.getColumnMap().get(col)));
		return isOriginal;
	    }
	}
	return false;
    }
    
    public void setOriginalSpreadsheetPath(String path) {
	this.quObject.setSpreadsheetPath(path);
    }
    /**
     * Gets the array of existing container collections in the repository
     * that do not present a generated Qudex Instance
     */
	public List<String> getQudexRootCollections() {
		try {
		    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
		    return mulgService.retrieveQudexRootCollections(Constants.QUDEX_PREFIX);
		} catch (JFigException e) {
		    log.error("qudexManager", e);
		    return null;
		}
	}
	
	/**
	 * UPDATE OPERATIONS
	 */
	
	public void updateCollections(InputStream spreadsheet) {
		//READ SPREADSHEET
	}
	
	public void updateFiles(InputStream spreadsheet) {
		//READ SPREADSHEET
	}

	public List<String> getQudexInstanceCollections() {
		try {
		    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
		    return mulgService.retrieveQudexInstanceCollections(Constants.QUDEX_PREFIX);
		} catch (JFigException e) {
		    log.error("qudexManager", e);
		    return null;
		}
	}

	public List<String> getQudexCollectionDocuments(String collectionId) {
		try {
		    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
		    return mulgService.retrieveQudexCollectionFiles(collectionId);
		} catch (JFigException e) {
		    log.error("qudexManager", e);
		    return null;
		}
	}

	public String getTitle(String fedoraPid) {
		try {
		    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
		    return mulgService.retrieveObjectTitle(fedoraPid);
		} catch (JFigException e) {
		    log.error("qudexManager", e);
		    return null;
		}
	}
	
	public void generateElementsExistingInstance(String qudexInstance, String collectionID) {
		FedoraMulgaraService mulgService;
		List<CategoryStruct> catList = new ArrayList<CategoryStruct>();
		try {
			mulgService = new FedoraMulgaraServiceImpl();
			QudexDocument qudex = QudexDocument.Factory.parse(qudexInstance);
			CategoryCollectionType catColl = qudex.getQudex().getCategoryCollection();
			for (CategoryType cat : catColl.getCategoryArray()) {
				String pid = mulgService.retrieveCategoryPID(cat.getDisplayLabel(), collectionID);
				if (pid == "")
					log.error("Category empty PID, method: generateElementsExistingInstance");
				CategoryStruct catS = new CategoryStruct();
				catS.setCategoryDisplayLabel(cat.getDisplayLabel());
				catS.setCategoryLabel(cat.getLabel());
				catS.setCategoryPid(pid);
				catS.setCategoryXmlId(cat.getId());
				catS.getDocumentRefsXmlIds().addAll(cat.getDocumentRefs());
				//AMG this is to not create the fedoraObject, instead update the existing
				//category object
				catS.setExistingCategory(true);
				catList.add(catS);
			}
			this.qudexManager.getCategoryManager().setElementsList(catList);
			
		} catch (JFigException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (XmlException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@SuppressWarnings("unchecked")
	public void checkExistingCategories(String qudexInstance, String collectionID) {
		FedoraMulgaraService mulgService;
		try {
			mulgService = new FedoraMulgaraServiceImpl();
			QudexDocument qudex = QudexDocument.Factory.parse(qudexInstance);
			CategoryCollectionType catColl = qudex.getQudex().getCategoryCollection();
			for (CategoryStruct cat : (List<CategoryStruct>) this.getQudexManager().getCategoryManager().getElementsList()) {
				String pid = mulgService.retrieveCategoryPID(cat.getCategoryDisplayLabel(), collectionID);
				if (pid == "")
					log.error("Category empty PID, method: generateElementsExistingInstance");
				else {
					//AMG this is to not create the fedoraObject, instead update the existing
					//category object
					for (CategoryType catT : catColl.getCategoryArray()) {
						if (catT.getDisplayLabel().equals(cat.getCategoryDisplayLabel())) {
							cat.setExistingCategory(true);
							cat.setCategoryPid(pid);
							cat.setCategoryXmlId(catT.getId());
							cat.getDocumentRefsXmlIds().addAll(catT.getDocumentRefs());
							break;
						}
					}
				}
			}			
		} catch (JFigException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (XmlException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
